package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 * @since 2023-11-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {
    private final ILearningLessonService lessonService;
    private final CourseClient courseClient;
    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 1、查询当前用户指定课程的学习进度(Day3 - 课堂)
     *
     * @param courseId 课程ID
     * @return
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        log.info("查询当前用户指定课程：{}的学习进度", courseId);
        //1、获取登录用户
        Long userId = UserContext.getUser();

        //2、查询课表
        //select * from learning_lesson where user_id = #{} and courseId = #{}
        LearningLesson lesson = lessonService.getLearningLesson(userId, courseId);
        if (ObjectUtil.isEmpty(lesson)) {
            throw new BadRequestException("课表信息不存在");
        }

        //3、查询学习记录
        // select * from learning_record where lesson_id = #{}
        List<LearningRecord> records = lambdaQuery().eq(LearningRecord::getLessonId, lesson.getId()).list();

        //4、封装结果并返回
        LearningLessonDTO dto = new LearningLessonDTO();
        dto.setId(lesson.getId());
        dto.setLatestSectionId(lesson.getLatestSectionId());
        dto.setRecords(BeanUtils.copyList(records, LearningRecordDTO.class));
        return dto;
    }

    /**
     * 2、提交学习记录（Day3 - 课堂）
     *
     * @param formDTO
     */
    @Override
    @Transactional
    public void addLearningRecord(LearningRecordFormDTO formDTO) {
        //1、处理学习提交记录
        boolean firstFinished;
        if (formDTO.getSectionType() == SectionType.VIDEO) {
            //1.1、处理视频
            firstFinished = handlerVideoRecord(formDTO);
        } else {
            //1.2、处理考试
            firstFinished = handlerExamRecord(formDTO);
        }
        if(!firstFinished && formDTO.getDuration() == -1){
            return;
        }

        //2、处理课表数据
        handlerLearningLessonsChanges(formDTO, firstFinished);
    }

    /**
     * 处理课表数据
     *
     * @param formDTO       前端参数
     * @param firstFinished 是否第一次学完
     */
    private void handlerLearningLessonsChanges(LearningRecordFormDTO formDTO, boolean firstFinished) {
        //1、查询课表
        LearningLesson lesson = lessonService.getById(formDTO.getLessonId());   //Mysql   0   1000   15
        if (ObjectUtil.isEmpty(lesson)) {
            throw new BizIllegalException("课表不存在，无法更新数据");
        }

        //2、判断时候有新的完成小节
        boolean allLearned = false;
        if (firstFinished) {
            //2.1、查询课程表
            CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (ObjectUtil.isEmpty(courseInfo)) {
                throw new BizIllegalException("课程不存在，无法更新数据");
            }
            //2.2、比较课程是否全部学完 ： 已学习小节 >= 课程总小节
            allLearned = lesson.getLearnedSections() + 1 >= courseInfo.getSectionNum();
        }

        //3、更新
        boolean success = lessonService.lambdaUpdate()
                .set(lesson.getStatus() == LessonStatus.NOT_BEGIN, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue()) // 如果课程下的小节全部学完，将课程状态改为：已学完
                .set(LearningLesson::getLatestSectionId, formDTO.getSectionId()) // 最近一次学习的小节ID（下次续播）
                .set(LearningLesson::getLatestLearnTime, formDTO.getCommitTime()) // 最近一次的学习时间
                //.set(firstFinished,LearningLesson::getLearnedSections,lesson.getLearnedSections() +1 ) // 如果该小节已经学完，将已学习的小节数 +1
                .setSql(firstFinished, "learned_sections = learned_sections + 1 ") // 如果该小节已经学完，将已学习的小节数 +1
                .eq(LearningLesson::getId, lesson.getId()) // 根据课表ID进行更新
                .update();
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
    }

    /**
     * 处理视频学习记录
     *
     * @param formDTO 前端参数
     * @return
     */
    private boolean handlerVideoRecord(LearningRecordFormDTO formDTO) {
        //1、查询旧的学习记录
        LearningRecord oldRecord = getOldRecord(formDTO);

        //2、判断是否存在
        if (ObjectUtil.isEmpty(oldRecord)) {
            //3、不存在，新增
            //3.1、转换Dto为PO
            LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);
            //3.2、填充数据
            record.setUserId(UserContext.getUser());
            //3.3、写入数据库
            boolean success = save(record);
            if (!success) {
                throw new DbException("新增学习记录失败！");
            }
            return false;
        }

        //4、存在，更新
        //4.1、判断是否是第一次学完
        boolean firstFinished = !oldRecord.getFinished() && formDTO.getMoment() * 2 >= formDTO.getDuration();
        //4.2、如果是非第一次学完，将学习进度暂存至redis，并且提交一个延时检测任务
        if(!firstFinished){
            LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);
            record.setId(oldRecord.getId());
            record.setFinished(oldRecord.getFinished());
            taskHandler.addLearningRecordTask(record);
            formDTO.setDuration(-1);
            return false;
        }

        //4.3、更新学习记录
        boolean updateResult = this.lambdaUpdate()
                .set(LearningRecord::getMoment, formDTO.getMoment())  //更新进度
                .set(LearningRecord::getFinished, true)  //更新是否完成
                .set(LearningRecord::getFinishTime, formDTO.getCommitTime())  //更新完成学习的时间
                .eq(LearningRecord::getId, oldRecord.getId()).update();
        if (!updateResult) {
            throw new DbException("更新学习记录失败！");
        }

        //4.4、清理redis缓存
        taskHandler.cleanLearningRecordCache(formDTO.getLessonId(),formDTO.getSectionId());

        //5、返回状态
        return true;
    }

    private LearningRecord getOldRecord(LearningRecordFormDTO formDTO) {
        //1、先从缓存中查询播放记录，看是否存在
        LearningRecord learningRecord = taskHandler.getLearningRecordFromCache(formDTO.getLessonId(),formDTO.getSectionId());

        //2、如果存在直接返回
        if(ObjectUtil.isNotEmpty(learningRecord)){
            return learningRecord;
        }

        //3、如果不存在，查询数据库，并且写入缓存
        learningRecord = this.lambdaQuery().eq(LearningRecord::getUserId, UserContext.getUser())
                .eq(LearningRecord::getSectionId, formDTO.getSectionId())
                .one();
        if(ObjectUtil.isEmpty(learningRecord)){
            return null;
        }
        taskHandler.writeLearningRecordToCache(learningRecord);
        return learningRecord;
    }

    /**
     * 处理考试学习记录
     *
     * @param formDTO 前端参数
     * @return
     */
    private boolean handlerExamRecord(LearningRecordFormDTO formDTO) {
        //1、转换Dto为PO
        LearningRecord record = BeanUtils.copyBean(formDTO, LearningRecord.class);

        //2、填充数据
        record.setUserId(UserContext.getUser());
        record.setFinished(true);
        record.setFinishTime(formDTO.getCommitTime());

        //3、写入数据库
        boolean success = save(record);
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
        return true;
    }
}
